<?php

namespace Modules\Xunsearch\Library;

use Modules\Core\Models\Log;
use Modules\Queue\Models\Queue;
use Phalcon\Config\Adapter\Ini as ConfigIni;
use Phalcon\Di;

class Xunsearch
{
    protected $_projectFile;
    protected $_project;
    protected $_projectInfo;
    protected $_search;

    public function __construct($project)
    {
        $project = ucfirst($project);
        $projectList = Di::getDefault()->getShared('config')->cache('searchProject');
        if (!isset($projectList[$project])) {
            throw new \Exception('搜索项目不存在：' . $project);
        }
        $this->_projectInfo = $projectList[$project];

        $project = strtolower($project);
        $this->_project = $project;
        $this->_projectFile = CONFIG_DIR . 'modules/xunsearch/' . $project . '.ini';
        if (!file_exists($this->_projectFile)) {
            self::generateProjectFile();
        }
        if (!file_exists($this->_projectFile)) {
            throw new \Exception('配置文件不存在：' . $this->_projectFile);
        }
    }

    public function getConfig()
    {
        $config = new ConfigIni($this->_projectFile);
        return $config;
    }

    public static function generateProjectFile()
    {
        $di = Di::getDefault();
        $projectList = $di->getShared('config')->cache('searchProject');
        foreach ($projectList as $project) {
            $callable = $project['callable'] . '::xunsearchGenerateProjectFile';
            if (isCallable($callable)) {
                $state = call_user_func($callable);
                if ($state !== true) {
                    Log::add([
                        'data' => '生成Xunsearch配置文件时，遇到不可调用回调函数：' . $callable,
                        'type' => 'xunsearch',
                        'level' => 2,
                    ]);
                }
            }
        }
    }

    public function update($item)
    {
        $doc = new \XSDocument();
        $doc->setFields($item);
        $xs = new \XS($this->_projectFile);
        $index = $xs->index;
        $index->update($doc);
        $index->closeBuffer();
    }

    public function delete($item, $subject = null)
    {
        $xs = new \XS($this->_projectFile);
        $index = $xs->index;
        if (is_null($subject)) {
            $index->del($item);
        } else {
            $index->del($item, $subject);
        }
        $index->closeBuffer();
    }

    public function query($query)
    {
        $xs = new \XS($this->_projectFile);
        $this->_search = $xs->search;
        return $this->_search->setFuzzy()->setQuery($query['conditions'])->setLimit($query['limit'])->search();
    }

    public function highlight($body)
    {
        return $this->_search->highlight($body);
    }

    public function total()
    {
        if (is_object($this->_search)) {
            return $this->_search->getLastCount();
        }
        return 1;
    }

    public function relatedSearches($num = 10, $word = null)
    {
        if (is_object($this->_search)) {
            return $this->_search->getRelatedQuery($word, $num);
        }
        return false;
    }

    public function hotSearches($num = 10)
    {
        if (!is_object($this->_search)) {
            $xs = new \XS($this->_projectFile);
            $this->_search = $xs->search;
        }
        return $this->_search->getHotQuery($num);
    }

    public function rebuild($queue = null)
    {
        $di = Di::getDefault();

        try {
            $sourceCallable = $this->_projectInfo['callable'] . '::source';
            if (!isCallable($sourceCallable)) {
                $error = '搜索项目Xunsearch数据源不可调用：' . $sourceCallable;
                throw new \Exception($error);
            }
            $totalCallable = $this->_projectInfo['callable'] . '::total';
            if (!isCallable($totalCallable)) {
                $error = '搜索项目Xunsearch数据源不可调用：' . $totalCallable;
                throw new \Exception($error);
            }

            $total = call_user_func($totalCallable);
            $data = call_user_func($sourceCallable);
            $currentCount = $defaultCount = count($data);
            $currentNum = 0;
            $base = 100 / $total;
            $page = 1;
            $xs = new \XS($this->_projectFile);
            $index = $xs->index;
            $index->beginRebuild();
            while ($data) {
                $currentCount = count($data);
                $currentNum += $currentCount;
                foreach ($data as $item) {
                    $docData = [];
                    foreach ($this->getConfig()->toArray() as $name => $value) {
                        if (is_array($value) && (isset($value['type']) || isset($value['index'])) && $name != 'id') {
                            $docData[$name] = is_string($item->{$name}) ? strip_tags($item->{$name}) : implode(',', objectToArray($item->{$name}));
                        }
                    }
                    $docData['id'] = (string) $item->getId();

                    $doc = new \XSDocument();
                    $doc->setFields($docData);
                    $index->add($doc);
                }
                if (!is_null($queue) && $queue instanceof Queue) {
                    $percentage = ($currentNum * $base) / ($total * $base);
                    $queue->percentage = floatval($queue->percentage + ($percentage / 100));
                    $queue->notice = $queue->notice . "，重建{$currentNum}条，共{$total}条";
                }
                $page++;
                $data = call_user_func($sourceCallable, $page);
            }
            $index->endRebuild();
        } catch (\Exception $e) {
            $error = '重建Xunsearch项目时出现错误 ' . $e->getMessage();
            Log::add([
                'data' => $error . ' File：' . $e->getFile() . ' Line：' . $e->getLine(),
                'type' => 'xunsearchQueue',
                'level' => 'warning',
            ]);
            throw new \Exception($error);
        }
    }

    public static function formFieldToIni($fields, $projectName)
    {
        $projectName = strtolower($projectName);
        $typeList = [
            'id' => 'id',
            'title' => 'title',
            'body' => 'body',
            'string' => 'string',
            'numeric' => 'numeric',
            'date' => 'date',
        ];
        $indexList = [
            'self' => 'self',
            'mixed' => 'mixed',
            'both' => 'both',
        ];
        $fields = formFieldInit($fields, false);
        $output = "project.name = {$projectName}\n\n";
        $output .= "[id]\n";
        $output .= "type = id\n\n";

        foreach ($fields as $name => $element) {
            if (!isset($element['xunsearch']) || !is_array($element['xunsearch'])) {
                continue;
            }
            echo $name . '<br />';
            $output .= "[{$name}]\n";

            if (isset($element['xunsearch']['type']) && isset($typeList[$element['xunsearch']['type']])) {
                $output .= "type = {$element['xunsearch']['type']}\n";
            }

            if (isset($element['xunsearch']['index']) && isset($indexList[$element['xunsearch']['index']])) {
                $output .= "index = {$element['xunsearch']['index']}\n";
            }

            if (isset($element['xunsearch']['cutlen'])) {
                $output .= "index = {$element['xunsearch']['index']}\n";
            }

            $output .= "\n";
        }
        //return File::createFile('Web/'.WEB_CODE.'/config/modules/xunsearch',$projectName.'.ini');
        return file_put_contents(CONFIG_DIR . 'modules/xunsearch/' . $projectName . '.ini', $output);
    }
}
